﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TransportAbstractionLibrary.Common;
using System.Net;
using System.Net.Sockets;

namespace TransportAbstractionLibrary.UDP
{
    public abstract partial class UDPServer
    {
        SocketEventArgs senderEventArgs;
        bool isSending;
        object sendingLocker;
        Queue<SendItem> sendQueue;

        private void InitializeSend()
        {
            sendingLocker = new object();
            senderEventArgs = bufferManager.CheckOut();
            senderEventArgs.Initialize(this, EventArgsMode.Send);
            readingEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, port);
            sendQueue = new Queue<SendItem>();
            isSending = false;
        }

        private void LoopData()
        {
            SendItem item;
            while (true)
            {
                lock (sendingLocker)
                {
                    if (sendQueue.Count > 0)
                    {
                        item = sendQueue.Dequeue();
                    }
                    else
                    {
                        isSending = false;
                        return;
                    }

                    senderEventArgs.RemoteEndPoint = item.Connection.EndPoint;
                    senderEventArgs.SetData(item.Data);
                    if (socket.SendToAsync(senderEventArgs))
                    {
                        return;
                    }

                }
            }

        }

        internal void SendData(UDPConnection connection, byte[] data)
        {
            if (data.Length > 64 * 1024)
            {
                throw new TALSocketException(connection, "Can't send data that is biggar than 64KB!");
            }
            bool found = false;
            lock (connectionListLocker)
            {
                for (int i = 0; i < connectionList.Count; i++)
                {
                    if (connectionList[i] == connection)
                    {
                        found = true;
                        break;
                    }
                }
            }
            if (!found)
            {
                throw new ApplicationException("Connection not found in connection list!");
            }

            lock (sendingLocker)
            {
                if (isSending)
                {
                    sendQueue.Enqueue(new SendItem(connection, data));
                    return;
                }
                else
                {
                    isSending = true;
                }
            }
            senderEventArgs.RemoteEndPoint = connection.EndPoint;
            senderEventArgs.SetData(data);
            if (!socket.SendToAsync(senderEventArgs))
            {
                LoopData();
            }
        }

        void IAsyncHost.OnSent(SocketEventArgs args)
        {
            if (args.SocketFlags != SocketFlags.None)
            {
                throw new ArgumentException(null, "SocketFlags are not None!, SocketFlags: " + args.SocketFlags.ToString());
            }
            LoopData();
        }

    }
}
